package pm.oo.util;

import com.sun.star.beans.PropertyValue;
import com.sun.star.frame.*;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.XURLTransformer;

import java.util.Map;

import pm.oo.util.OfficeConnection;

/**
 * @author Anton Bernatski
 */
public class Executor {

	public static void dispatch(XFrame xFrame, final String url, Map<String, Object> properties) {

		PropertyValue[] lProperties;
		if (properties != null) {
			lProperties = new PropertyValue[properties.size()];
			int i = 0;
			for (String propertyName : properties.keySet()) {
				lProperties[i] = new PropertyValue();
				lProperties[i].Name = propertyName;
				lProperties[i].Value = properties.get(propertyName);
				i++;
			}
		} else {
			lProperties = new PropertyValue[0];
		}
		exec(xFrame, url, lProperties);
	}

	private static void exec(XFrame xFrame, final String url, PropertyValue[] properties) {
		com.sun.star.util.URL aURL = parseURL(url);
		if (aURL == null) {
			return;
		}
		if (xFrame == null) {
			return;
		}
		execute(xFrame, aURL, properties, null);
	}

	/**
	 * This convert an URL (formated as a string) to a struct com.sun.star.util.URL.
	 * It use a special service to do that: the URLTransformer.
	 * Because some API calls need it and it's not allowed to set "Complete"
	 * part of the util struct only. The URL must be parsed.
	 *
	 * @param stringURL URL for parsing in string notation
	 * @return [com.sun.star.util.URL]
	 *         URL in UNO struct notation
	 */
	private static com.sun.star.util.URL parseURL(String stringURL) {

		if (stringURL == null || stringURL.equals("")) {
			throw new RuntimeException("wrong using of URL parser");
		}

		com.sun.star.util.URL url;
		try {
			XComponentContext xOfficeCtx = OfficeConnection.getOfficeContext();

			// Create special service for parsing of given URL.
			Object transformerObj = xOfficeCtx.getServiceManager().createInstanceWithContext(
					"com.sun.star.util.URLTransformer", xOfficeCtx);
			XURLTransformer xParser = (XURLTransformer) UnoRuntime.queryInterface(XURLTransformer.class, transformerObj);

			// Because it's an in/out parameter we must use an array of URL objects.
			com.sun.star.util.URL[] aParseURL = new com.sun.star.util.URL[1];
			aParseURL[0] = new com.sun.star.util.URL();
			aParseURL[0].Complete = stringURL;

			// Parse the URL
			xParser.parseStrict(aParseURL);

			url = aParseURL[0];
		}
		catch (com.sun.star.uno.RuntimeException exRuntime) {
			// Any UNO method of this scope can throw this exception.
			// Reset the return value only.
			url = null;
		}
		catch (com.sun.star.uno.Exception exUno) {
			// "createInstance()" method of used service manager can throw it.
			// Then it wasn't possible to get the URL transformer.
			// Return default instead of realy parsed URL.
			url = null;
		}

		return url;
	}

	/**
	 * Dispatch an URL to given frame.
	 * Caller can register himself for following status events for dispatched
	 * URL too. But nobody guarantee that such notifications will occure.
	 * (see dispatchWithNotification() if you interest on that)
	 * The returned dispatch object should be hold alive by caller
	 * till he deosn't need it any longer. Otherwise the dispatcher can(!)
	 * die by decreasing his refcount.
	 *
	 * @param xFrame	  frame wich should be the target of this dispatch
	 * @param aURL		full parsed and converted office URL for dispatch
	 * @param lProperties optional arguments for dispatch
	 * @param xListener   optional listener which is registered automaticly for status events
	 *                    (Note: Deregistration is part of this listener himself!)
	 * @return [XDispatch] It's the used dispatch object and can be used for deregistration of an optional listener.
	 *         Otherwhise caller can ignore it.
	 */
	private static XDispatch execute(XFrame xFrame,
									 com.sun.star.util.URL aURL,
									 com.sun.star.beans.PropertyValue[] lProperties,
									 XStatusListener xListener) {
		XDispatch xDispatcher = null;

		try {
			// Query the frame for right interface which provides access to all available dispatch objects.
			XDispatchProvider xProvider = (XDispatchProvider) UnoRuntime.queryInterface(XDispatchProvider.class, xFrame);

			// Ask himn for right dispatch object for given URL.
			// Force given frame as target for following dispatch by using "".
			// It means the same like "_self".
			xDispatcher = xProvider.queryDispatch(aURL, "", 0);

			// Dispatch the URL into the frame.
			if (xDispatcher != null) {
				if (xListener != null) {
					xDispatcher.addStatusListener(xListener, aURL);
				}

				xDispatcher.dispatch(aURL, lProperties);
			}
		}
		catch (com.sun.star.uno.RuntimeException exUno) {
			// Any UNO method of this scope can throw this exception.
			// But there will be nothing to do then - because
			// we haven't changed anything inside the remote objects
			// except method "addStatusListener().
			// But in this case the source of this exception has to
			// rollback all his operations. There is no chance to
			// make anything right then.
			// Reset the return value to a default - that's it.
			exUno.printStackTrace();
			xDispatcher = null;
		}

		return xDispatcher;
	}

	/**
	 * Dispatch an URL to given frame.
	 * Caller can register himself for following result events for dispatched
	 * URL too. Notifications are guaranteed (instead of dispatch())
	 * Returning of the dispatch object isn't neccessary.
	 * Nobody must hold it alive longer the dispatch needs.
	 *
	 * @param xFrame	  frame wich should be the target of this dispatch
	 * @param aURL		full parsed and converted office URL for dispatch
	 * @param lProperties optional arguments for dispatch
	 * @param xListener   optional listener which is registered automaticly for status events
	 *                    (Note: Deregistration is not supported. Dispatcher does it automaticly.)
	 */
	private static void executeWithNotification(XFrame xFrame,
												com.sun.star.util.URL aURL,
												com.sun.star.beans.PropertyValue[] lProperties,
												XDispatchResultListener xListener) {
		try {
			// Query the frame for right interface which provides access to all available dispatch objects.
			XDispatchProvider xProvider = (XDispatchProvider) UnoRuntime.queryInterface(XDispatchProvider.class, xFrame);

			// Ask himn for right dispatch object for given URL.
			// Force THIS frame as target for following dispatch.
			// Attention: The interface XNotifyingDispatch is an optional one!
			XDispatch xDispatcher = xProvider.queryDispatch(aURL, "", 0);
			XNotifyingDispatch xNotifyingDispatcher = (XNotifyingDispatch) UnoRuntime
					.queryInterface(XNotifyingDispatch.class, xDispatcher);

			// Dispatch the URL.
			if (xNotifyingDispatcher != null) {
				xNotifyingDispatcher.dispatchWithNotification(aURL, lProperties, xListener);
			}
		}
		catch (com.sun.star.uno.RuntimeException exUno) {
			// Any UNO method of this scope can throw this exception.
			// But there is nothing we can do then.
			exUno.printStackTrace();
		}
	}
}
